#include "GEnginePCH.h"

#include "Support.h"
#include "ParticleEmitter.h"
struct PART_VERTEX
{
	float x, y, z;
	D3DCOLOR color;
	float tu, tv;
	float size;
};


ParticleEmitter::ParticleEmitter()
{
	// default particle system configuration - boring!
	this->SetGravity(D3DXVECTOR3(0.0f, -10.0f,0.0f));
	SetPos(D3DXVECTOR3(0.0f,0.0f,0.0f));
	SetMinEmitRate(200.0f);
	SetMaxEmitRate(200.0f);
	SetMinSize(1.0f);
	SetMaxSize(10.0f);
	SetMinLife(1.0f);
	SetMaxLife(4.0f);
	SetColor1(D3DXCOLOR(0.4f,1.0f,0.0f,1.0f));
	SetColor2(D3DXCOLOR(1.0f,0.1f,0.0f,1.0f));

	SetSpawnDir1(D3DXVECTOR3(-1.0f, -1.0f, -1.0f));
	SetSpawnDir2(D3DXVECTOR3(1.0f, 1.0f, 1.0f));

	// initialize misc. other things
	d3ddev = NULL;
	SetVBSize((int)(NUMPARTICLES / 1.0f) * 4);
	m_fNumNewPartsExcess = 0.0f;

}

ParticleEmitter::~ParticleEmitter()
{
}

void ParticleEmitter::Update(float fTimeDelta)
{

	// update existing particles
	{
		for (int q=0; q < m_Particles.GetTotalElements(); q++) {
			if (m_Particles.IsAlive(q)) {
				Particle &part = m_Particles.GetAt(q);

				if (!part.Update(fTimeDelta)) {
					m_Particles.Delete(&part);
				}
				part.m_vDir += m_vGravity * fTimeDelta;
			} // is alive
		} // next particle
	}


	// create new particles
	{
		// determine the number of particles we need to create

		float fEmitRateThisFrame = RandomFloat(m_fMinEmitRate, m_fMaxEmitRate);
		int iNumNewParts =(int)(fEmitRateThisFrame * fTimeDelta);
		m_fNumNewPartsExcess += (float)(fEmitRateThisFrame * fTimeDelta)-iNumNewParts;

		if (m_fNumNewPartsExcess > 1.0f) 
		{
			iNumNewParts += (int)m_fNumNewPartsExcess;
			m_fNumNewPartsExcess -= (int)m_fNumNewPartsExcess;
		}

		if ( iNumNewParts > m_Particles.GetNumFreeElements() )
		{
			iNumNewParts=m_Particles.GetNumFreeElements();
		}
		for (int q=0; q < iNumNewParts; q++) 
		{
			try 
			{
				Particle *part = m_Particles.New();

				part->m_vDir = RandomVector(m_vSpawnDir1, m_vSpawnDir2);
				
				float fRandR = RandomFloat(m_Color1.r, m_Color2.r);
				float fRandG = RandomFloat(m_Color1.g, m_Color2.g);
				float fRandB = RandomFloat(m_Color1.b, m_Color2.b);
				float fRandA = RandomFloat(m_Color1.a, m_Color2.a);

				part->m_Color = D3DXCOLOR(fRandR, fRandG, fRandB, fRandA);
				part->m_ColorStep = (m_Color2-m_Color1)/part->m_fLifetime;
				part->m_vPos = m_vPos;
				part->m_fLifetime=RandomFloat(m_fMinLife, m_fMaxLife);

			} catch(...) { q = iNumNewParts; }
		}
	}
}
// #define D3DFVF_CUSTOMVERTEX ( D3DFVF_XYZ | D3DFVF_TEX1 )
HRESULT ParticleEmitter::Render( const D3DXMATRIX& matView, const D3DXMATRIX& matVP)
{
	HRESULT hr;



	D3DXMATRIX world;
	D3DXMatrixIdentity(&world);
	
	// Set up the vertex buffer to be rendered
	d3ddev->SetStreamSource( 0, m_vbParticles, 0, sizeof(PART_VERTEX) );

	//d3ddev->SetFVF( D3DFVF_CUSTOMVERTEX );
	d3ddev->SetIndices(m_ibParticles);

	d3ddev->SetTexture(0, m_texParticle);

	PART_VERTEX *pVertices;
	short *pIndices;
	DWORD dwNumParticlesToRender = 0;
	

	if(FAILED(hr = m_vbParticles->Lock(0, m_iVBSize * sizeof(PART_VERTEX),
		(void **) &pVertices, 0)))
	{
		return hr;
	}	
	if(FAILED(hr = m_ibParticles->Lock(0, (int)(sizeof(short)*m_iVBSize*6/4.0f), (void **) &pIndices, 0)))
	{
		return hr;
	}

	int k=0;
	// Render each particle

	short* aux=pIndices;

	//Billboard
	D3DXMATRIX matBillboard, matViewTrans;

	D3DXMatrixTranspose(&matViewTrans, &matView);
	D3DXMatrixIdentity(&matBillboard);

	matBillboard._11 = matViewTrans._11;
	matBillboard._12 = matViewTrans._12;
	matBillboard._13 = matViewTrans._13;
	matBillboard._21 = matViewTrans._21;
	matBillboard._22 = matViewTrans._22;
	matBillboard._23 = matViewTrans._23;
	matBillboard._31 = matViewTrans._31;
	matBillboard._32 = matViewTrans._32;
	matBillboard._33 = matViewTrans._33;
	//Billboard


	D3DXMATRIX matVPTransposed, matTransform;
	D3DXMatrixTranspose(&matBillboard, &matBillboard);
	d3ddev->SetVertexShaderConstantF(0, matBillboard, 4);	
	
	D3DXMatrixTranspose(&matVPTransposed, &matVP);
	d3ddev->SetVertexShaderConstantF(4, matVPTransposed, 4);	
	
	for (int q=0; q < NUMPARTICLES; q++) {
		
		// Render each particle a bunch of times to get a blurring effect
		if (m_Particles.IsAlive(q)) 
		{
			Particle &part = m_Particles.GetAt(q);
			

			pVertices->x = part.m_vPos.x;
			pVertices->y = part.m_vPos.y;
			pVertices->z = part.m_vPos.z;
			pVertices->color = (DWORD)part.m_Color;
			pVertices->tu = 0.0f;
			pVertices->tv = 0.0f;
			pVertices->size  = part.m_fSize;
			pVertices++;
			

			pVertices->x = part.m_vPos.x;
			pVertices->y = part.m_vPos.y;
			pVertices->z = part.m_vPos.z;
			pVertices->color = (DWORD)part.m_Color;	
			pVertices->tu = 1.0f;
			pVertices->tv = 0.0f;
			pVertices->size  = part.m_fSize;
			pVertices++;




			pVertices->x = part.m_vPos.x;
			pVertices->y = part.m_vPos.y;
			pVertices->z = part.m_vPos.z;
			pVertices->color = (DWORD)part.m_Color;
			pVertices->tu = 0.0f;
			pVertices->tv = 1.0f;
			pVertices->size  = part.m_fSize;
			pVertices++;



			pVertices->x = part.m_vPos.x;
			pVertices->y = part.m_vPos.y;
			pVertices->z = part.m_vPos.z;
			pVertices->color = (DWORD)part.m_Color;
 			pVertices->tu = 1.0f;
			pVertices->tv = 1.0f;
			pVertices->size  = part.m_fSize;
			pVertices++;




 			*pIndices++ =(short)( k  );  //v0
 			*pIndices++ =(short)( k+1 ); //v1 
 			*pIndices++ =(short)( k+2 ); //v2 
 
			*pIndices++ =(short)( k+1  ); //v1
			*pIndices++ =(short)( k+3 ); //v3 
			*pIndices++ =(short)( k+2 ); //v2 

			





			if( (++dwNumParticlesToRender)*4 == m_iVBSize ) 
			{
				// Done filling this chunk of the vertex buffer.  Lets unlock and
				// draw this portion so we can begin filling the next chunk.

				m_vbParticles->Unlock();
				m_ibParticles->Unlock();



				if(FAILED(hr = d3ddev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0,
							dwNumParticlesToRender*4, 0, dwNumParticlesToRender * 2 )))
					return hr;

				if(FAILED(hr = m_vbParticles->Lock(0, m_iVBSize * sizeof(PART_VERTEX),
					(void **) &pVertices, 0)))
				{
					return hr;
				}
				if(FAILED(hr = m_ibParticles->Lock(0, (int)(sizeof(short)*m_iVBSize*6/4.0f), (void **) &pIndices, 0)))
				{
					return hr;
				}

				dwNumParticlesToRender = 0;
				k=0;
			}
			else 
			{
				k+=4;
			}
		}
	}

	// Unlock the vertex buffer
	m_vbParticles->Unlock();
	m_ibParticles->Unlock();

	// Render any remaining particles
	if( dwNumParticlesToRender )
	{
		if(FAILED(hr = d3ddev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0,
				dwNumParticlesToRender*4, 0, dwNumParticlesToRender * 2 )))
		{
			return hr;
		}
	}


	return S_OK;

}


HRESULT ParticleEmitter::RestoreDeviceObjects( const char *strTextureFilename)
{
	HRESULT hr;
	m_Particles.DeleteAll();

	// load up the particle system texture
	if (FAILED(hr = D3DXCreateTextureFromFile(d3ddev, strTextureFilename, &m_texParticle))) 
	{
		return(hr);
	}

	// create vertex buffer

	if(FAILED(hr = d3ddev->CreateVertexBuffer(m_iVBSize * sizeof(PART_VERTEX), 
		D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC , 
		NULL, D3DPOOL_DEFAULT, &m_vbParticles, NULL))) 
	{
		return(hr);
	}

	if(FAILED(d3ddev->CreateIndexBuffer((int)(sizeof(short)*m_iVBSize*6/4.0f),
		D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC,
		D3DFMT_INDEX16, D3DPOOL_DEFAULT,
		&m_ibParticles,	NULL)))
	{
		return(hr);
	}	
	return S_OK;	
}

void ParticleEmitter::InvalidateDeviceObjects()
{
	if( m_texParticle != NULL )
	{
		int nNewRefCount = m_texParticle->Release();

		if( nNewRefCount > 0 )
		{
			static char strError[255];
			sprintf_s( strError, "The Particle Texture object failed to cleanup properly.\n"
				"Release() returned a reference count of %d", nNewRefCount );
			MessageBox( NULL, strError, "ERROR", MB_OK | MB_ICONEXCLAMATION );
		}

		m_texParticle = NULL;
	}

	if( m_vbParticles != NULL )
	{
		int nNewRefCount = m_vbParticles->Release();

		if( nNewRefCount > 0 )
		{
			static char strError[255];
			sprintf_s( strError, "The Particle Emitter Vertex Buffer object failed to cleanup properly.\n"
				"Release() returned a reference count of %d", nNewRefCount );
			MessageBox( NULL, strError, "ERROR", MB_OK | MB_ICONEXCLAMATION );
		}

		m_vbParticles = NULL;
	}

	if( m_ibParticles != NULL )
	{
		int nNewRefCount = m_ibParticles->Release();

		if( nNewRefCount > 0 )
		{
			static char strError[255];
			sprintf_s( strError, "The Particle Emitter Index Buffer object failed to cleanup properly.\n"
				"Release() returned a reference count of %d", nNewRefCount );
			MessageBox( NULL, strError, "ERROR", MB_OK | MB_ICONEXCLAMATION );
		}

		m_ibParticles = NULL;
	}

}	
void ParticleEmitter::SetRenderStates()
{

	d3ddev->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);

//	d3ddev->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATER );
//	d3ddev->SetRenderState( D3DRS_ALPHAREF, 127 );
	//d3ddev->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );

 	 d3ddev->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
 	 d3ddev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
 	 d3ddev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );

}
void ParticleEmitter::ResetRenderStates()
{
	// Reset render states
	//d3ddev->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );
	d3ddev->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
	d3ddev->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
}